package com.beta.resource.services;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.stereotype.Service;

import com.beta.resource.mapper.CategoryMapper;
import com.beta.resource.mapper.CodeFileMapper;
import com.beta.resource.mapper.UserMapper;
import com.beta.resource.model.Category;
import com.beta.resource.model.Page;
import com.beta.resource.model.UserEnv;
import com.beta.resource.model.UserFile;
import com.beta.resource.model.UserInfo;
import com.beta.resource.utils.Security;
import com.beta.resource.utils.onStatusChange;

@Service
public class UserEnvService {
    @Value("${env.code.file.title}")
    private String DEFAULT_USERFILE_TITLE;
    @Value("${env.code.file.desc}")
    private String DEFAULT_USERFILE_DESC;
    @Value("${env.code.user.prefix}")
    private String PREFIX_USERID;
    @Value("${codefile.savedir}")
    private String CODE_SAVE_DIR;
    @Value("${codefile.notes.default}")
    private String DEFAULT_NOTES_CODE;
    @Value("${env.ui.editor.title}")
    private String DEFAULT_CODE_TITLE;
    @Value("${env.ui.editor.page.item}")
    private int CODE_PAGE_ITEMS_NUM;
    @Value("${env.ui.editor.page.num}")
    private int CODE_PAGE_NUM;
    @Value("${security.token.introspect}")
    private String TOKEN_INSTROSPECT_URL;
    @Value("${security.client-id}")
    private String OAUTH2_CLIENT_ID;
    @Value("${security.client-secret}")
    private String OAUTH2_CLIENT_SECRET;
    @Autowired
    private UserMapper mUserMapper;
    @Autowired
    private CategoryMapper mCategoryMapper;
    @Autowired
    private CodeFileMapper mCodeFileMapper;

    private Map<String, UserEnv> mUserMap = new HashMap<>();
    
    private Logger log = LoggerFactory.getLogger(getClass());

    @PostConstruct
    public void postConstruct() {
        mCategoryMapper.createTables();
        mCategoryMapper.createUserDefTable();
        mCodeFileMapper.createTable();
        mUserMapper.createTable();
    }

    public UserEnv getEnv(JwtAuthenticationToken token) {
        UserInfo userinfo = new UserInfo(token);
        UserEnv env = mUserMap.get(userinfo.getSuid());
        if(env == null){
            env = new UserEnv(createNewUser(userinfo));
            mUserMap.put(userinfo.getSuid(), env);
        }else{
            env.updateInfo(userinfo);
        }
        return env;
    }

    private UserInfo createNewUser(UserInfo info) {
        Integer id = mUserMapper.getLocalUser(info.getSuid());
        if (id == null) {
            info.setSuid(info.getSuid());
            mUserMapper.createNewLocalUser(info);
        } else {
            info.setUid(id);
        }
        return info;
    }

    public int getUID(JwtAuthenticationToken token){
        return getEnv(token).getUID();
    }

    // public String getToken(JwtAuthenticationToken token){
    //     return getEnv(token).getInfo().getToken();
    // }

    public List<Category> getCategories(JwtAuthenticationToken token, boolean encode) {
        return getEnv(token).getCategories(mCategoryMapper, encode);
    }

    public List<UserFile> getCodeFiles(JwtAuthenticationToken token, String categoryDesc, int offset) {
        return getEnv(token).getCodeFiles(mCategoryMapper, mCodeFileMapper, categoryDesc, offset, CODE_PAGE_ITEMS_NUM);
    }

    public int getRemainingFiles(JwtAuthenticationToken token, int offset) {
        return getEnv(token).getRemainingFiles(offset, mCodeFileMapper, CODE_PAGE_ITEMS_NUM);
    }

    public UserFile loadFile(JwtAuthenticationToken token, int fid, onStatusChange listener, Object data) throws IOException {
        return getEnv(token).loadFile(fid, mCodeFileMapper, listener, data);
    }

    public UserFile loadFile(String suid, int fid, onStatusChange listener, Object data) throws IOException{
        if(mUserMap.containsKey(suid)){
            return mUserMap.get(suid).loadFile(fid, mCodeFileMapper, listener, data);
        }
        return null;
    }

    public UserFile loadFileInfo(int uid, int fid) {
        return mCodeFileMapper.getFileInfo(fid, uid);
    }

    public int saveFile(JwtAuthenticationToken token, UserFile file) {
        return getEnv(token).saveFile(file, mCategoryMapper, mCodeFileMapper, CODE_SAVE_DIR, DEFAULT_NOTES_CODE);
    }

    public List<Page> calcCurrentPage(JwtAuthenticationToken token, int start, int index, int remaining) {
        return getEnv(token).calcCurrentPage(start, index, remaining, CODE_PAGE_NUM, CODE_PAGE_ITEMS_NUM);
    }

    public String getPageTitle(JwtAuthenticationToken token) {
        return getEnv(token).getPageTitle(DEFAULT_USERFILE_TITLE, PREFIX_USERID);
    }

    public UserFile getDefaultUserFile(JwtAuthenticationToken token) {
        return getEnv(token).getDefaultUserFile(DEFAULT_USERFILE_TITLE, DEFAULT_USERFILE_DESC);
    }

    public boolean verifyToken(String suid, String mechatoken){
        log.debug("verifyToken:"+suid+", token:"+mechatoken);
        for(String k:mUserMap.keySet()){
            log.debug(k);
        }
        if(!mUserMap.containsKey(suid)){
            return false;
        }
        UserEnv env = mUserMap.get(suid);
        String token = env.getInfo().getToken(mechatoken);
        if(token.isEmpty()){
            return false;
        }
        Boolean ret = Security.verfiyOAuthToken(TOKEN_INSTROSPECT_URL, OAUTH2_CLIENT_ID, OAUTH2_CLIENT_SECRET, env.getInfo().getUsername(),token);
        return ret == null? false:ret.booleanValue();
    }
}
